package com.example.myblog_springboot.controller;

import com.example.myblog_springboot.service.MyblogUserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RAtomicLong;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.time.LocalTime;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Controller
@RequestMapping("/v1/datasets/redission")
@Tag(name = "用户管理接口")
@CrossOrigin(origins = "*")
@Slf4j
public class MyblogUserController {

    // 限流相关常量定义 - 增加了接口标识，支持多接口独立限流
    private static final String RATE_LIMIT_LOCK_PREFIX = "api:rate:limit:lock:";  // 限流锁键前缀
    private static final String RATE_LIMIT_COUNTER_PREFIX = "api:rate:limit:counter:";  // 计数器键前缀
    private static final String RATE_LIMIT_TIMESTAMP_PREFIX = "api:rate:limit:timestamp:";  // 时间戳键前缀

    // 接口标识 - 用于区分不同接口的限流
    private static final String API_IDENTIFIER = "user:retrieve";

    // 高峰期限流配置：2个请求/秒
    private static final int PEAK_MAX_CALLS = 2;
    private static final int PEAK_INTERVAL_SECONDS = 1;

    // 非高峰期限流配置：8个请求/分钟
    private static final int NON_PEAK_MAX_CALLS = 8;
    private static final int NON_PEAK_INTERVAL_SECONDS = 60;

    @Autowired
    private MyblogUserService myblogUserService;

    @Autowired
    private RedissonClient redissonClient;

    @RequestMapping(value = "/{datasetId}/retrieve", method = RequestMethod.POST)
    @Operation(summary = "获取用户信息列表")
    @ResponseBody
    public Map<String, Object> getUser(@PathVariable String datasetId) {
        HashMap<String, Object> resultMap = new HashMap<>();
        String data = null;
        String message = "success";
        int code = 200;

        try {
            // 判断是否允许调用，传入datasetId使限流可以更精细化
            if (canInvokeApi(datasetId)) {
                data = myblogUserService.getString();
                message = isNonPeakTime() ?
                        "接口调用成功（非高峰期）" : "接口调用成功（高峰期）";
                // 模拟接口处理时间
                TimeUnit.SECONDS.sleep(1);
            } else {
                // 超出限流限制
                message = isNonPeakTime() ?
                        String.format("接口调用失败：非高峰期超出每%d秒%d次限制",
                                NON_PEAK_INTERVAL_SECONDS, NON_PEAK_MAX_CALLS) :
                        String.format("接口调用失败：高峰期超出每%d秒%d次限制",
                                PEAK_INTERVAL_SECONDS, PEAK_MAX_CALLS);
                code = 429;  // 429 Too Many Requests
                log.warn("接口限流: {}, datasetId: {}", API_IDENTIFIER, datasetId);
            }

        } catch (Exception e) {
            message = "接口调用异常：" + e.getMessage();
            code = 500;
            log.error("接口调用异常", e);
        }

        resultMap.put("data", data);
        resultMap.put("msg", message);
        resultMap.put("code", code);
        return resultMap;
    }

    /**
     * 判断是否可以调用接口（基于分布式锁的限流实现）
     * @param datasetId 数据集ID，用于更精细化的限流
     */
    private boolean canInvokeApi(String datasetId) {
        // 构建针对当前接口和数据集的唯一键
        String lockKey = RATE_LIMIT_LOCK_PREFIX + API_IDENTIFIER + ":" + datasetId;
        String counterKey = RATE_LIMIT_COUNTER_PREFIX + API_IDENTIFIER + ":" + datasetId;
        String timestampKey = RATE_LIMIT_TIMESTAMP_PREFIX + API_IDENTIFIER + ":" + datasetId;

        RLock lock = redissonClient.getLock(lockKey);
        try {
            // 尝试获取锁，最多等待1秒，10秒后自动释放
            boolean locked = lock.tryLock(1, 10, TimeUnit.SECONDS);
            if (!locked) {
                // 获取锁失败，视为限流（防止锁竞争导致的问题）
                log.debug("获取限流锁失败: {}", lockKey);
                return false;
            }

            // 获取当前限流配置
            int maxCalls;
            int intervalSeconds;
            if (isNonPeakTime()) {
                maxCalls = NON_PEAK_MAX_CALLS;
                intervalSeconds = NON_PEAK_INTERVAL_SECONDS;
            } else {
                maxCalls = PEAK_MAX_CALLS;
                intervalSeconds = PEAK_INTERVAL_SECONDS;
            }

            // 使用原子类操作，保证计数准确性
            RAtomicLong timestampCounter = redissonClient.getAtomicLong(timestampKey);
            RAtomicLong requestCounter = redissonClient.getAtomicLong(counterKey);

            long currentWindowStartTime = timestampCounter.get();
            long currentCount = requestCounter.get();
            long now = System.currentTimeMillis() / 1000;  // 秒级时间戳

            // 判断是否需要重置计数器（进入新的时间窗口）
            if (currentWindowStartTime == 0 || now - currentWindowStartTime >= intervalSeconds) {
                // 重置时间窗口和计数器
                timestampCounter.set(now);
                requestCounter.set(1);
                // 设置过期时间，自动清理
                timestampCounter.expire(intervalSeconds + 1, TimeUnit.SECONDS);
                requestCounter.expire(intervalSeconds + 1, TimeUnit.SECONDS);
                log.debug("重置限流窗口: {}, 新计数: 1", API_IDENTIFIER);
                return true;
            }

            // 在当前时间窗口内，递增计数器
            currentCount = requestCounter.incrementAndGet();

            // 检查是否超过最大调用次数
            if (currentCount > maxCalls) {
                log.debug("超出限流: {}, 当前计数: {}, 最大限制: {}",
                        API_IDENTIFIER, currentCount, maxCalls);
                return false;
            }

            log.debug("接口调用计数: {}, 当前计数: {}, 最大限制: {}",
                    API_IDENTIFIER, currentCount, maxCalls);
            return true;

        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.error("限流判断被中断", e);
            return false;
        } finally {
            // 确保锁被释放
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    /**
     * 判断当前是否为非业务高峰期（18:00:00-次日08:00:00）
     */
    private boolean isNonPeakTime() {
        LocalTime now = LocalTime.now();
        LocalTime peakStart = LocalTime.of(8, 0, 0);    // 高峰期开始时间：08:00:00
        LocalTime peakEnd = LocalTime.of(18, 0, 0);      // 高峰期结束时间：18:00:00

        // 非高峰期是指不在 08:00:00-18:00:00 之间的时间
        return now.isBefore(peakStart) || now.isAfter(peakEnd);
    }
}
